package logger

import (
	"fmt"
	"gitee.com/vrv_media/go-micro-framework/pkg/common/util/sliceutil"
	"github.com/spf13/pflag"
)

const (
	flagLevel             = "log.level"
	flagDisableCaller     = "log.disable-caller"
	flagDisableStacktrace = "log.disable-stacktrace"
	flagFormat            = "log.format"
	flagEnableColor       = "log.enable-color"
	flagOutputPaths       = "log.output-paths"
	flagErrorOutputPaths  = "log.error-output-paths"
	flagDevelopment       = "log.development"
	flagName              = "log.name"
	fileSegment           = "log.enable-file-segment"
	fileSegmentMaxSize    = "log.file-segment.max-size"
	fileSegmentMaxAge     = "log.file-segment.max-age"
	fileSegmentMaxBackups = "log.file-segment.max-backups"

	consoleFormat = "console"
	jsonFormat    = "json"

	stdoutPath = "stdout"
	stderrPath = "stderr"
)

var (
	levelAllows  = []string{"debug", "info", "warn", "error"}
	formatAllows = []string{consoleFormat, jsonFormat}
)

// Options 日志的配置类，推荐使用NewOptions方法创建默认的配置，然后再根据配置文件进行填充个性化配置
type Options struct {
	// 日志级别，选项(debug,info,warn,error,dpanic,panic,fatal)
	Level string `json:"level"              mapstructure:"level"`
	// json的格式，选项（console，json）
	Format string `json:"format"             mapstructure:"format"`
	// 停止使用调用函数的文件名和行号注释日志, 在默认情况下 所有日志都有注释
	DisableCaller bool `json:"disable-caller"     mapstructure:"disable-caller"`
	// 禁用自动捕获堆栈跟踪。默认情况下，在 dev 环境中捕获WarnLevel及以上级别的日志，在 pro 环境中捕获ErrorLevel及以上级别的日志
	DisableStacktrace bool `json:"disable-stacktrace" mapstructure:"disable-stacktrace"`
	EnableColor       bool `json:"enable-color"       mapstructure:"enable-color"`
	// 是否是开发模式
	Development bool `json:"development"        mapstructure:"development"`
	// 日志名称
	Name string `json:"name"               mapstructure:"name"`
	//是否开启traceID
	EnableTraceID bool `json:"enable-trace-id"     mapstructure:"enable-trace-id"`
	//是否开启traceStack
	EnableTraceStack bool `json:"enable-trace-stack" mapstructure:"enable-trace-stack"`
	// 输出 默认输出到标准输出,如果不是标准输出，则认为是文件输出
	OutputPaths []string `json:"output-paths"       mapstructure:"output-paths"`
	// 错误输出 默认输出到标准错误
	ErrorOutputPaths []string `json:"error-output-paths" mapstructure:"error-output-paths"`

	//是否开启文件分割,默认不分割
	EnableFileSegment bool `json:"enable-file-segment"     mapstructure:"enable-file-segment"`

	// 文件分割策略
	FileSegment FileSegmentOptions `json:"file-segment" mapstructure:"file-segment"`
}

type FileSegmentOptions struct {
	// MaxSize 进行切割之前，日志文件的最大大小(MB为单位)，默认为100MB
	MaxSize int `json:"max-size" mapstructure:"max-size"`
	// MaxAge 是根据文件名中编码的时间戳保留旧日志文件的最大天数,默认为30天。
	MaxAge int `json:"max-age" mapstructure:"max-age"`
	// MaxBackups 是要保留的旧日志文件的最大数量。默认是保留所有旧的日志文件（尽管 MaxAge 可能仍会导致它们被删除。）
	MaxBackups int `json:"max-backups" mapstructure:"max-backups"`
}

type Option func(*Options)

func NewOptions(opts ...Option) *Options {
	defaultOptions := &Options{
		Level:             "info",
		DisableCaller:     false,
		DisableStacktrace: false,
		Format:            consoleFormat,
		EnableColor:       false,
		Development:       false,
		EnableTraceID:     false,
		EnableTraceStack:  false,
		OutputPaths:       []string{"stdout"},
		ErrorOutputPaths:  []string{"stderr"},
		EnableFileSegment: true,
		FileSegment: FileSegmentOptions{
			MaxSize: 100,
			MaxAge:  30,
		},
	}
	if len(opts) == 0 {
		return defaultOptions
	}
	for _, opt := range opts {
		opt(defaultOptions)
	}
	return defaultOptions
}

func WithDebugLevel() Option {
	return func(o *Options) {
		o.Level = "debug"
	}
}
func WithInfoLevel() Option {
	return func(o *Options) {
		o.Level = "info"
	}
}

func WithConsoleFormat() Option {
	return func(o *Options) {
		o.Format = consoleFormat
	}
}

func WithJsonFormat() Option {
	return func(o *Options) {
		o.Format = jsonFormat
	}
}

func WithColor(on bool) Option {
	return func(o *Options) {
		o.EnableColor = on
	}
}

func WithOutPutPath(outPutPath, errorOutPutPath []string) Option {
	return func(o *Options) {
		o.OutputPaths = outPutPath
		o.ErrorOutputPaths = errorOutPutPath
	}
}

func WithFileSegment(on bool) Option {
	return func(o *Options) {
		o.EnableFileSegment = on
	}
}

func WithFileSegmentOption(maxSize, maxAge, maxBackups int) Option {
	return func(o *Options) {
		o.FileSegment.MaxSize = maxSize
		o.FileSegment.MaxAge = maxAge
		o.FileSegment.MaxBackups = maxBackups
	}
}

func (o *Options) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&o.Level, flagLevel, o.Level, "Minimum log output `LEVEL`.")
	fs.BoolVar(&o.DisableCaller, flagDisableCaller, o.DisableCaller, "Disable output of caller information in the log.")
	fs.BoolVar(&o.DisableStacktrace, flagDisableStacktrace, o.DisableStacktrace, "Disable the log to record a stack trace for all messages at or above panic level.")
	fs.StringVar(&o.Format, flagFormat, o.Format, "Log output `FORMAT`, support plain or json format.")
	fs.BoolVar(&o.EnableColor, flagEnableColor, o.EnableColor, "Enable output ansi colors in plain format logs.")
	fs.StringSliceVar(&o.OutputPaths, flagOutputPaths, o.OutputPaths, "Output paths of log.")
	fs.StringSliceVar(&o.ErrorOutputPaths, flagErrorOutputPaths, o.ErrorOutputPaths, "Error output paths of log.")
	fs.BoolVar(&o.Development, flagDevelopment, o.Development,
		"Development puts the logger in development mode, which changes the behavior of DPanicLevel and takes stacktraces more liberally.",
	)
	fs.StringVar(&o.Name, flagName, o.Name, "The name of the logger.")
	fs.BoolVar(&o.EnableFileSegment, fileSegment, o.EnableFileSegment, "是否开启文件分割.")
	fs.IntVar(&o.FileSegment.MaxSize, fileSegmentMaxSize, o.FileSegment.MaxSize, "进行切割之前，日志文件的最大大小(MB为单位)，默认为100MB")
	fs.IntVar(&o.FileSegment.MaxAge, fileSegmentMaxAge, o.FileSegment.MaxAge, "是根据文件名中编码的时间保留旧日志文件的最大天数,默认为30天。")
	fs.IntVar(&o.FileSegment.MaxBackups, fileSegmentMaxBackups, o.FileSegment.MaxBackups, "是要保留的旧日志文件的最大数量。默认是保留所有旧的日志文件（尽管 MaxAge 可能仍会导致它们被删除。）")
}

func (o *Options) Validate() error {
	if sliceutil.HasNotIn(o.Level, levelAllows) {
		return fmt.Errorf("invalid log level: %s,allows:%v", o.Level, levelAllows)
	}
	if sliceutil.HasNotIn(o.Format, formatAllows) {
		return fmt.Errorf("invalid log format: %s,allows:%v", o.Format, formatAllows)
	}
	if len(o.OutputPaths) == 0 {
		return fmt.Errorf("no output paths")
	}
	if len(o.ErrorOutputPaths) == 0 {
		return fmt.Errorf("no error output paths")
	}
	return nil
}
